package club.janna.rpcdemo.server.listener;

import club.janna.rpcdemo.codec.Codec;
import club.janna.rpcdemo.server.codec.RPCDecoder;
import club.janna.rpcdemo.server.codec.RPCEncoder;
import club.janna.rpcdemo.domain.Request;
import club.janna.rpcdemo.domain.Response;
import club.janna.rpcdemo.server.register.ServiceRegister;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;

@Slf4j
public class NettyNetListener implements NetListener {

    @Value("${rpc.port}")
    private int port;
    @Autowired
    private Codec codec;
    @Autowired
    private ServiceRegister serviceRegister;

    @PostConstruct
    public void init() {
        bind();
    }

    @Override
    public void bind() {
        ServerBootstrap bootstrap = new ServerBootstrap();
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        RPCDecoder rpcDecoder = new RPCDecoder(codec);
        RPCEncoder rpcEncoder = new RPCEncoder(codec);
        RPCHandler rpcHandler = new RPCHandler(serviceRegister);
        bootstrap.group(bossGroup, workGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        // 报文最大100k，2个字节报文头，4字节报文长度，跳过6个字节
                        socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(102400, 2, 4, 0, 6));
                        socketChannel.pipeline().addLast(rpcDecoder);
                        socketChannel.pipeline().addLast(rpcEncoder);
                        socketChannel.pipeline().addLast(rpcHandler);
                    }
                });
        try {
            ChannelFuture future = bootstrap.bind(port).sync();
            log.info("Netty 服务已启动，监听端口：{}", port);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理RPC请求
     */
    @Slf4j
    @ChannelHandler.Sharable
    private static class RPCHandler extends ChannelInboundHandlerAdapter {
        private ServiceRegister serviceRegister;
        public RPCHandler(ServiceRegister serviceRegister) { this.serviceRegister = serviceRegister; }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            Request request = (Request) msg;
            log.debug("收到请求 -> {}", request);
            if(request == null)
                throw new RuntimeException("request is null");

            Response response = new Response();

            // 获取到对应的bean
            Object bean = serviceRegister.getBean(request.getServiceName());
            if(bean == null) {
                log.warn("请求的服务不存在");
                response.setState(Response.State.NOT_FOUND);
                ctx.write(response);
                return;
            }
            Method method = bean.getClass().getMethod(request.getMethod(), request.getParameterTypes());
            if(method == null) {
                log.warn("请求的接口不存在");
                response.setState(Response.State.NOT_FOUND);
                ctx.write(response);
                return;
            }

            try {
                // 调用对应的方法，获取到返回的结果
                Object result = method.invoke(bean, request.getArgs());
                response.setResult(result);
                response.setState(Response.State.OK);
            } catch (Throwable e) {
                response.setState(Response.State.ERROR);
                response.setError(e);
            }
            // 发送response给客户端
            ctx.writeAndFlush(response);
        }
    }
}
